# -*- coding: iso-8859-2 -*-

import os
import logging
import traceback
import re
import string
import sys
import datetime
import copy
import random

from time import time

from authorshipanalyzer.message import Message
from authorshipanalyzer.message import MessageReader
from authorshipanalyzer.file import Path
from authorshipanalyzer.textmodifier import MessagePreprocessor
from authorshipanalyzer.message import MessageRepository
from authorshipanalyzer.slot import SlotManager
from authorshipanalyzer.utils import getKey
from authorshipanalyzer.utils import xuniqueCombinations
from authorshipanalyzer.utils import boolConverter
from authorshipanalyzer.authorslot import AuthorSlotManager
from authorshipanalyzer.classifier import Selector
from authorshipanalyzer.classifier import Classifier
from authorshipanalyzer.experiments import AuthorExperiment

class AuthorAnalyzer(object):
    
    def __init__(self, argv):
        self.argv = argv
    
    def analyze(self):           
        if len(self.argv) != 2:
            self.usage()
            sys.exit(-1)
            
        properties = self.readProperties()
        
        # Experiment handler
        experiment = AuthorExperiment()
        
        # Read slots
        slots = []
        
        authorsToRetrieve = {}
        if properties["authors"]:
            authorsToRetrieve = copy.deepcopy(properties["authors"])
            for author, value in properties["unknown"].items():
                key = getKey(authorsToRetrieve, author)
                if key != None:
                    authorsToRetrieve[key] += value
                else:
                    authorsToRetrieve[author] = value
             
        print "\n"
        print "Retrieving messages and slot creating..."
        start = time()
        if properties["read.repo"] == "true":
            msgRepository = MessageRepository()
            slots = msgRepository.load(authorsToRetrieve)
        else:
            fromItems = properties["read.from"].split("-")
            fromDate = datetime.date(int(fromItems[0]), int(fromItems[1]), 1)
            
            toItems = properties["read.to"].split("-")
            toDate = datetime.date(int(toItems[0]), int(toItems[1]), 3)
            
            mr = MessageReader(Path.getMainDir() + os.sep + "info1")
            messages = mr.readMessages(fromDate, toDate)
            
            sm = SlotManager()
            sm.createSlots(messages, authorsToRetrieve)
            slots = sm.getSlots()
            
            if properties["read.store"] == "true":
                msgRepository = MessageRepository()
                msgRepository.store(slots)
                
        end = time()    
        
        print "\n"
        print "Slots retrieved in %f seconds" % (end - start)
        print "Number of messages: %d" % len(slots)
        
        # Check slots
        retreivedAuthors = {}        
        for slot in slots:
            if retreivedAuthors.has_key(slot.author):
                retreivedAuthors[slot.author] += 1
            else:
                retreivedAuthors[slot.author] = 1
        print "Number of authors: %d" % len(retreivedAuthors.keys())
        
        for key, value in retreivedAuthors.items():
            print key + " " + str(value)
        
        if authorsToRetrieve == {}:
            authorsToRetrieve = properties["unknown"]
            
        print "\n"
        print "Slot table:"
        print "%20s %15s %15s %15s" % ("author", "retrieved", "defined", "sufficient")
        for author in authorsToRetrieve.keys():
            sufficient = "OK"
            if retreivedAuthors[author] - authorsToRetrieve[author] < 0:
                sufficient = "X"
            print "%20s %15s %15s %15s" % (author, retreivedAuthors[author], authorsToRetrieve[author], sufficient)
        
        # for each known author size 10, 40, 70, 100, 130, 160, 190, 220, 250
        for knownAuthorCount in [ 10, 40, 70, 100, 130, 160, 190, 220, 250 ]:

            # slots of authors    
            authorSlots = {}            # dict<author, slots>, all slots
            authorSlotCombinations = {} # dict<author, random_slots>, chosen slots
            
            # select and classify
            for author, counter in properties["unknown"].items():
                
                # counter of experiments for a single author
                expCounter = 0
                
                print "Separating messages of %s from the rest..." % author
                for slot in slots:
                    if slot.author == author:
                        if not authorSlots.has_key(author):
                            authorSlots[author] = []
                        authorSlots[author].append(slot)
                          
                print "Random slots..."
                combinations = []
                for i in range(int(properties['validation.max'])):
                    combination = []
                    while (len(combination) < counter):
                        rInt = random.randint(0, len(authorSlots[author]) - 1)
                        if rInt not in combination:
                            combination.append(rInt)
                    combinations.append(combination)
                
                print "Creating slots combinations..."                    
                slotCombinations = []
                for combination in combinations:
                    slotCombination = []
                    for cIndex in combination:
                        slotCombination.append(authorSlots[author][cIndex])
                    slotCombinations.append(slotCombination)
                authorSlotCombinations[author] = slotCombinations
            
            print "Create unknown authors slots..."
            knownAuthors = []
            for author in properties["unknown"].keys():
                knownAuthors.append(author)
            for retAuthor in retreivedAuthors.keys():
                if len(knownAuthors) >= knownAuthorCount:
                    break
                
                if retAuthor not in knownAuthors:
                    knownAuthors.append(retAuthor)
                print "Choose %s as %d author..." % (retAuthor, len(knownAuthors) )
                
            
            knownSlots = []
            for knownSlot in slots:
                if knownSlot.author in knownAuthors:
                    knownSlots.append(copy.deepcopy(knownSlot))
                    
            for kSlot in knownSlots:
                for author, slotCombinations in authorSlotCombinations.items():
                    if kSlot.author == author:
                        for slotCombination in slotCombinations:
                            for uSlot in slotCombination:
                                if kSlot.text == uSlot.text and kSlot in knownSlots:
                                    knownSlots.remove(kSlot)
                                    break
        
            print "Create author slots..." 
            knownAsm = AuthorSlotManager.AuthorSlotManager()
            knownAsm.addSlots(knownSlots)
            knownAsm.recompute()
            print "Created %d known author slots..." % len(knownAsm.getAuthorSlots())
                
            print "Start analyzing..."
            for author, slotCombinations in authorSlotCombinations.items():
                
                knownAsmTemp = None
                
                for unknownSlots in slotCombinations:
                    
                    if properties["dump"] == "true":
                        self.storeSlots(unknownSlots)
                                    
                    knownAsmTemp = copy.deepcopy(knownAsm)
                                    
                    for i in range(int(properties["test.size"]), 0, -1):
                    
                        if len(unknownSlots) > i:
                            for j in range(i, len(unknownSlots)):
                                del unknownSlots[0]
                            
                        unknownAsm = AuthorSlotManager.AuthorSlotManager()
                        unknownAsm.addSlots(unknownSlots)
                        unknownAsm.recompute()
                    
                        print "\n"
                        print "-----------------------------------------------"
                        print "Compare..."
                        
                        print "\n"
                        print "Known slots:"
                        for author, authorSlot in knownAsm.getAuthorSlots().items():
                            print "%s, %d messages" % ( author, len(authorSlot.slots) )
                           
                        print "\n" 
                        print "Unknown slot:"
                        for author, authorSlot in unknownAsm.getAuthorSlots().items():
                            print "%s, %d messages" % ( author, len(authorSlot.slots) )
                            
                        print "\n"
                        print "Selection started..."
                        print "\n"
                    
                        # Dismiss these author whose sociolinguistic profile does not suit to the
                        # unknown profile
                        selector = Selector()
                        dismissed = selector.select(unknownAsm.getAuthorSlots().values()[0], \
                                                    knownAsm.getAuthorSlots().values())
                        
                        print "\n"
                        print "Selection finished!"
                        
                        for disAuthor in dismissed:
                            knownAsm.removeAuthor(disAuthor)
                        
                        print "%d authors removed!" % len(dismissed)
                        
                        # Classify
                        # features - features used in classification
                        # kernel - linear, gaussian, polynomial or tr
                        print "Classification..."
                        print "---------> %d " % (len(unknownSlots))
                        classifier = Classifier(properties["features"], properties["kernel"])
                        result = classifier.classify(unknownAsm.getAuthorSlots().values()[0], \
                                                            knownAsm.getAuthorSlots().values())
                        experiment.addResult(unknownAsm.getAuthorSlots().values()[0].author, len(unknownSlots), \
                                             knownAuthorCount, result)
                        experiment.printResults()
                        
                        # restore slots which were changed because of selection
                        if len(dismissed) > 0:        
                            knownAsm = copy.deepcopy(knownAsmTemp)
                
                # Validation:
                # - cross (each combination will be checked)
                # - single (single experiment)
                if properties["validation"] == "single":
                    break     
                
                # if maximum number of experiments for single author is reached, break
                if int(properties["validation.max"]) == expCounter:
                    break
                
                expCounter += 1
        
    def storeSlots(self, unknownSlots):
        '''
        Store slots in a file
        '''
        
        print "Store selected slots..."
        d = datetime.datetime.now()
        output = "output_%s_%d" % ( d.strftime("%y%m%d_%H.%M.%S"), random.randint(0, sys.maxint) )
        output = os.path.join(Path.getResourceDir(), "dump", output)
        outputFile = file(output, "w")
        for uSlot in unknownSlots:
            outputFile.write("Author: " + uSlot.author + "\n")
            outputFile.write("Subject: " + uSlot.message.getSubject() + "\n")
#            outputFile.write("Date " + str(uSlot.message.getDate()) + "\n\n")
            outputFile.write("\n\n")
            outputFile.write(uSlot.message.getText())
            outputFile.write("\n\n")
            outputFile.write("-----------------------------------------------")
            outputFile.write("\n\n")
        outputFile.flush()
        outputFile.close()
        print "Slots stored in %s" % output      

    def usage(self):
        print "Usage: <experiment_file>"
        
    def readProperties(self):
        '''
        Read program property file
        '''
        result = {}
        
        print "\n"
        print "Read properties from %s" % self.argv[1]
        
        features = {}
        
        file = open(os.path.join(Path.getMainDir(), self.argv[1]), "r")
        for line in file.readlines():
            line = line.strip()
            if line != "":
                items = line.split("=")
                assert len(items) == 2
                
                if items[0] == "authors" or items[0] == "unknown":
                    # if no author specified
                    if items[1] == "":
                        result[items[0]] = {}
                    else:
                        authorDict = {}
                        authors = items[1].split(",")
                        for author in authors:
                            aPrms = author.split(":")
                            assert len(aPrms) == 2
                            authorDict[aPrms[0]] = int(aPrms[1])
                        result[items[0]] = authorDict
                elif re.match("features.*", items[0]):
                    features[items[0]] = boolConverter(items[1])
                else:            
                    result[items[0]] = items[1];
        file.close()
        
        result["features"] = features
        return result
    
if __name__ == "__main__":
    AuthorAnalyzer(sys.argv).analyze()
    
